package com.hy.video.monitor.vmanager.gb28181.play;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hy.video.monitor.common.StreamInfo;
import com.hy.video.monitor.gb28181.bean.SsrcTransaction;
import com.hy.video.monitor.gb28181.session.VideoStreamSessionManager;
import com.hy.video.monitor.gb28181.bean.Device;
import com.hy.video.monitor.gb28181.transmit.callback.DeferredResultHolder;
import com.hy.video.monitor.gb28181.transmit.callback.RequestMessage;
import com.hy.video.monitor.media.zlm.ZLMRESTfulUtils;
import com.hy.video.monitor.media.zlm.dto.MediaServerItem;
import com.hy.video.monitor.service.IMediaServerService;
import com.hy.video.monitor.storager.IRedisCatchStorage;
import com.hy.video.monitor.vmanager.bean.VMCResult;
import com.hy.video.monitor.vmanager.gb28181.play.bean.PlayResult;
import com.hy.video.monitor.service.IMediaService;
import com.hy.video.monitor.service.IPlayService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.hy.video.monitor.gb28181.transmit.cmd.impl.SIPCommander;
import com.hy.video.monitor.storager.IVideoManagerStorager;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.List;
import java.util.UUID;

/**
 * @Classname PlayController
 * @Description TODO 国标设备点播
 * @Date 2022/3/21 11:10
 * @Created by KangJonney
 */
@Api(tags = "国标设备点播")
@CrossOrigin
@RestController
@RequestMapping("/api/play")
public class PlayController {

    private final static Logger logger = LoggerFactory.getLogger(PlayController.class);

    @Autowired
    private SIPCommander cmder;

    @Autowired
    private VideoStreamSessionManager streamSession;

    @Autowired
    private IVideoManagerStorager storager;

    @Autowired
    private IRedisCatchStorage redisCatchStorage;

    @Autowired
    private ZLMRESTfulUtils zlmresTfulUtils; //主动调用ZLM restful api

    @Autowired
    private DeferredResultHolder resultHolder; //异步请求处理句柄

    @Autowired
    private IPlayService playService; //点播处理

    @Autowired
    private IMediaService mediaService; //媒体信息业务

    @Autowired
    private IMediaServerService mediaServerService; //流媒体服务业务

    /**
     * 通知设备上传媒体流,通知设备推流
     * zs-monitor 先发起呼叫流程得到了ssrc和stream id， 再返回url给前端播放， 同时摄像头会推流给zlm
     * DeferredResult:异步请求
     * 异步查找流：查找一个流，当这个流还没在线(主播未推流)，我们不能立即掐断播放器，因为这个主播在几秒内会推流上来，
     * 也就是我们可以先播放再推流，这样客户端不会等待推流完了再打开播放器，这样极大的提高了客户端的体验感。
     * <p>
     * 适用基于rtp 1对1推流的物联网开发，因为他很难确定推流上来的时机，这样使用异步就会弥补等待推流的时间。
     * <p>
     * 播放器、信令服务对接流媒体流程：
     * 1、zs-monitor 通过 gegSystemCfg 函数和 zlm 连接，握手
     * 2、前端请求镜头，会把国标号码发给zs-monitor, zs-monitor 可以根据国标号码找到对应的镜头，可以通过rtsp、或国标协议发命令给zlm。
     * zlm可以rtsp拉流，或者国标收流，把视频流接入zlm
     * 3、zlm 会返回对应镜头的flv等拉流地址url 给zs-monitor ,zs-monitor 的网页就可以播放视频了。
     *
     * @param deviceId
     * @param channelId
     * @return
     */
    @ApiOperation("开始点播")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class),
            @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class),
    })
    @GetMapping("/start/{deviceId}/{channelId}")
    public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId,
                                                       @PathVariable String channelId) {

        // 获取可用的zlm
        Device device = storager.queryVideoDevice(deviceId);
        MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
        PlayResult playResult = playService.play(newMediaServerItem, deviceId, channelId, null, null, null);

        return playResult.getResult();
    }


    @ApiOperation("停止点播")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class),
            @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class),
    })
    @GetMapping("/stop/{deviceId}/{channelId}")
    public DeferredResult<ResponseEntity<String>> playStop(@PathVariable String deviceId, @PathVariable String channelId) {
        logger.debug(String.format("设备预览/回放停止API调用，streamId：%s_%s", deviceId, channelId ));

        String uuid = UUID.randomUUID().toString();
        DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>();

        // 录像查询以channelId作为deviceId查询
        String key = DeferredResultHolder.CALLBACK_CMD_STOP + deviceId + channelId;
        resultHolder.put(key, uuid, result);
        Device device = storager.queryVideoDevice(deviceId);
        StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
        if (streamInfo == null) {
            RequestMessage msg = new RequestMessage();
            msg.setId(uuid);
            msg.setKey(key);
            msg.setData("点播未找到");
            resultHolder.invokeAllResult(msg);
            storager.stopPlay(deviceId, channelId);
            return result;
        }
        cmder.streamByeCmd(deviceId, channelId, streamInfo.getStream(), (event) -> {
            redisCatchStorage.stopPlay(streamInfo);
            storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
            RequestMessage msg = new RequestMessage();
            msg.setId(uuid);
            msg.setKey(key);
            //Response response = event.getResponse();
            msg.setData(String.format("success"));
            resultHolder.invokeAllResult(msg);
        });

        if (deviceId != null || channelId != null) {
            JSONObject json = new JSONObject();
            json.put("deviceId", deviceId);
            json.put("channelId", channelId);
            RequestMessage msg = new RequestMessage();
            msg.setId(uuid);
            msg.setKey(key);
            msg.setData(json.toString());
            resultHolder.invokeAllResult(msg);
        } else {
            logger.warn("设备预览/回放停止API调用失败！");
            RequestMessage msg = new RequestMessage();
            msg.setId(uuid);
            msg.setKey(key);
            msg.setData("streamId null");
            resultHolder.invokeAllResult(msg);
        }

        // 超时处理
        result.onTimeout(()->{
            logger.warn(String.format("设备预览/回放停止超时，deviceId/channelId：%s_%s ", deviceId, channelId));
            RequestMessage msg = new RequestMessage();
            msg.setId(uuid);
            msg.setKey(key);
            msg.setData("Timeout");
            resultHolder.invokeAllResult(msg);
        });
        return result;
    }


    @ApiOperation("获取所有的ssrc")
    @GetMapping("/ssrc")
    public VMCResult<JSONObject> getSSRC() {
        if (logger.isDebugEnabled()) {
            logger.debug("获取所有的ssrc");
        }
        JSONArray objects = new JSONArray();
        List<SsrcTransaction> allSsrc = streamSession.getAllSsrc();
        for (SsrcTransaction transaction :  allSsrc) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("deviceId", transaction.getDeviceId());
            jsonObject.put("channelId", transaction.getChannelId());
            jsonObject.put("ssrc", transaction.getSsrc());
            jsonObject.put("streamId", transaction.getStream());
            objects.add(jsonObject);
        }

        VMCResult<JSONObject> result = new VMCResult<>();
        result.setCode(0);
        result.setMsg("success");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("data", objects);
        jsonObject.put("count", objects.size());
        result.setData(jsonObject);
        return result;
    }


}

